// ----------------
// Project:
// ESA Elevator
// ----------------
//
// Description:
// ----------------
// user_interface.v testbench
//
// Version History:
// ----------------
// 140131: added task to kill simulation if necessary
// 140131: added comment simulation start/finish
// 140131: delay between request and check relative to NUM_FLOORS
// 140131: waiting delays relative to localparam CLK_PERIOD
// 140131: added seperators as comments
// 140116:

`timescale 1ns / 1ps

module user_interface_tb_public;

  //****************** SIMULATION PARAMETERS *******************//  
  localparam CLK_PERIOD  = 50; // [ns] -> 20 MHz
  localparam NUM_FLOORS  = 99; // number of floors
  localparam FLOOR_BITS  = min_1(ceil_log2(NUM_FLOORS-1));
  //***********************************************************//

  //********************* MODULE INPUTS ***********************// 
  // Inputs
  reg CLK;
  reg RESET;
  reg [FLOOR_BITS-1:0] CURRENT_FLOOR;
  reg HALTED;
  reg [NUM_FLOORS-1:0] FLOOR_SELECT;
  reg CLOSE_DOOR;
  reg OPEN_DOOR;
  reg PASSENGER_ALARM;
  reg [(NUM_FLOORS*2)-1:0] FLOOR_REQUEST;
  //***********************************************************//  

  //********************* MODULE OUTPUTS **********************//
  // Outputs
  wire [NUM_FLOORS-1:0] ENLIGHT_BUTTONS;
  wire [13:0] ENABLE_SEGMENT;  
  wire [7:0] ASCI_FLOOR_HIGH;
  wire [7:0] ASCI_FLOOR_LOW;  
  wire [3:0] BINARY_FLOOR;
  wire [(NUM_FLOORS*2)-1:0] ENLIGHT_FLOOR_BUTTONS;
  wire [FLOOR_BITS-1:0] NEXT_FLOOR;
  wire [FLOOR_BITS-1:0] CURRENT_FLOOR_OUT;
  //***********************************************************// 

  //******************* UUT INSTANTIATION *********************// 
  user_interface #(.FLOORS(99), .FLOOR_BITS(7), .DISPLAY_SEGMENTS(14)) uut (
    .CLK(CLK), 
    .RESET(RESET), 
    .CURRENT_FLOOR(CURRENT_FLOOR), 
    .HALTED(HALTED), 
    .FLOOR_SELECT(FLOOR_SELECT), 
    .CLOSE_DOOR(CLOSE_DOOR),
    .OPEN_DOOR(OPEN_DOOR),
    .PASSENGER_ALARM(PASSENGER_ALARM), 
    .FLOOR_REQUEST(FLOOR_REQUEST), 
    .ENLIGHT_BUTTONS(ENLIGHT_BUTTONS), 
    .ENABLE_SEGMENT(ENABLE_SEGMENT), 
    .ENLIGHT_FLOOR_BUTTONS(ENLIGHT_FLOOR_BUTTONS), 
    .NEXT_FLOOR(NEXT_FLOOR),
    .CURRENT_FLOOR_OUT(CURRENT_FLOOR_OUT)
  );
  
  segment_encoder ssencoder_i(
      .ssIn(ENABLE_SEGMENT),
      .asciOutHigh(ASCI_FLOOR_HIGH),
      .asciOutLow(ASCI_FLOOR_LOW));
  //***********************************************************//

  //******************* TESTBENCH SIGNALS *********************// 
   reg [FLOOR_BITS-1:0] expected_next_floor;
   reg [13:0] enlight_buttons;
   reg [(FLOOR_BITS-1)*2-1:0] enlight_floor_button;
  //***********************************************************//   

  //******************* 20 MHz CLOCK SIGNAL *******************//   
  always begin 
    #(CLK_PERIOD/2) CLK = ~CLK;
  end
  //***********************************************************//  

  //********************* TEST INITIATION *********************//  
  initial begin
    $display("************ STARTING SIMULATION ************"); 
    // Initialize Inputs
    CLK             = 0;
    RESET           = 0;
    CURRENT_FLOOR   = 0;
    //DIRECTION     = 0;
    HALTED          = 1;
    FLOOR_SELECT    = 0;
    CLOSE_DOOR      = 0;
    PASSENGER_ALARM = 0;
    FLOOR_REQUEST   = 0;

    // Wait 100 ns for global reset to finish
    #(2*CLK_PERIOD);
    RESET = 1;
    #(2*CLK_PERIOD);
    RESET = 0;
    //--------------------------
    // TARGET FLOOR 1 
    //--------------------------
    //DIRECTION     = 0;
    CURRENT_FLOOR = 0;
    HALTED        = 1;
    FLOOR_SELECT  = 5'b00010;
    FLOOR_REQUEST = 10'b0000000000;
    CLOSE_DOOR = 0;
    PASSENGER_ALARM = 0;
    #(2*(NUM_FLOORS+1)*CLK_PERIOD);
    expected_next_floor = 1;
    //HALTED        = 0;
    #(10*CLK_PERIOD);
    //--------------------------
    // TARGET FLOOR 1 OK -> NEW TARGET FLOOR 2 
    //--------------------------
    //DIRECTION     = 0;
    CURRENT_FLOOR = 1;
    HALTED        = 1;
    FLOOR_SELECT  = 5'b00100;
    FLOOR_REQUEST = 10'b0000000000;
    CLOSE_DOOR = 0;
    PASSENGER_ALARM = 0;
    #(2*(NUM_FLOORS+1)*CLK_PERIOD);
    expected_next_floor = 2;
    //HALTED        = 0;
    #(10*CLK_PERIOD);
    //--------------------------
    // ADD TARGET FLOOR 4, REQUEST UP FROM FLOOR 0 
    //--------------------------
    //DIRECTION     = 0;
    CURRENT_FLOOR = 2;
    HALTED        = 1;
    FLOOR_SELECT  = 5'b10000;
    FLOOR_REQUEST = 10'b0000001000; //change up(0) to up(1)
    CLOSE_DOOR = 0;
    PASSENGER_ALARM = 0;
    #(2*(NUM_FLOORS+1)*CLK_PERIOD);
    expected_next_floor = 4;
    //HALTED        = 0;
    #(10*CLK_PERIOD);
    //--------------------------
    // CLEAR TARGET FLOOR 2, REQUEST UP FROM FLOOR 3 
    //--------------------------
    //DIRECTION     = 0;
    CURRENT_FLOOR = 2;
    HALTED        = 1;
    FLOOR_SELECT  = 5'b00000;
    FLOOR_REQUEST = 10'b0010000000;
    CLOSE_DOOR = 0;
    PASSENGER_ALARM = 0;
    #(2*(NUM_FLOORS+1)*CLK_PERIOD);
    expected_next_floor = 3;
    //HALTED        = 0;
    #(10*CLK_PERIOD);
    //--------------------------
    // CLEAR TARGET FLOOR 3, ADD TARGET FLOOR 1
    //--------------------------
    //DIRECTION     = 0;
    CURRENT_FLOOR = 3;
    HALTED        = 1;
    FLOOR_SELECT  = 5'b00010;
    FLOOR_REQUEST = 10'b0000000000;
    CLOSE_DOOR = 0;
    PASSENGER_ALARM = 0;
    #(2*(NUM_FLOORS+1)*CLK_PERIOD);
    expected_next_floor = 4;
    //HALTED        = 0;
    #(10*CLK_PERIOD);
    //--------------------------
    // CLEAR TARGET FLOOR 4
    //--------------------------
    //DIRECTION     = 1;
    CURRENT_FLOOR = 4;
    HALTED        = 1;
    FLOOR_SELECT  = 5'b00000;
    FLOOR_REQUEST = 10'b0000000000;
    CLOSE_DOOR = 0;
    PASSENGER_ALARM = 0;
    #(2*(NUM_FLOORS+1)*CLK_PERIOD);
    expected_next_floor = 1;
    //HALTED        = 0;
    #(10*CLK_PERIOD);
    //--------------------------
    // REQUEST DOWN FROM FLOOR 2, UP FROM FLOOR 3
    //--------------------------
    //DIRECTION     = 1;
    CURRENT_FLOOR = 3;
    HALTED        = 0;
    FLOOR_SELECT  = 5'b00000;
    FLOOR_REQUEST = 10'b0010010000;
    CLOSE_DOOR = 0;
    PASSENGER_ALARM = 0;
    #(2*(NUM_FLOORS+1)*CLK_PERIOD);
    expected_next_floor = 2;
    //HALTED        = 0;
    #(10*CLK_PERIOD);
    //--------------------------
    // CLEAR TARGET FLOOR 2
    //--------------------------
    //DIRECTION     = 1;
    CURRENT_FLOOR = 2;
    HALTED        = 1;
    FLOOR_SELECT = 5'b00000;
    CLOSE_DOOR = 0;
    PASSENGER_ALARM = 0;
    FLOOR_REQUEST = 10'b0000000000;
    #(2*(NUM_FLOORS+1)*CLK_PERIOD);
    expected_next_floor = 1;
    #(10*CLK_PERIOD);
    //--------------------------
    // CLEAR TARGET FLOOR 1
    //--------------------------
    //DIRECTION     = 0;
    CURRENT_FLOOR = 1;
    HALTED        = 1;
    FLOOR_SELECT = 5'b00000;
    FLOOR_REQUEST = 10'b0000000000;
    CLOSE_DOOR = 0;
    PASSENGER_ALARM = 0;
    #(2*(NUM_FLOORS+1)*CLK_PERIOD);
    expected_next_floor = 3;
    //HALTED        = 0;
    #(10*CLK_PERIOD);
    //--------------------------
    // CLEAR TARGET FLOOR 3
    //--------------------------
    //DIRECTION     = 0;
    CURRENT_FLOOR = 3;
    HALTED        = 1;
    FLOOR_SELECT = 5'b00000;
    FLOOR_REQUEST = 10'b0000000000;
    CLOSE_DOOR = 0;
    PASSENGER_ALARM = 0;
    #(2*(NUM_FLOORS+1)*CLK_PERIOD);
    expected_next_floor = 3;
    //HALTED        = 0;
    #(10*CLK_PERIOD);
    $display("************ SIMULATION COMPLETE ************");
    $finish;
  end
  //***********************************************************// 
  
initial begin : kill
  integer i;
  for (i = 0; i < 10000; i=i+1)
  begin
	#(1000000*CLK_PERIOD);
  end
  $display("************ SIMULATION KILLED BECAUSE OF ERROR ************");
  $finish;
end

  //****************** VISUALISATION PROCESS ******************// 
  always @(NEXT_FLOOR) begin
    if(CURRENT_FLOOR != NEXT_FLOOR) begin
      $display("Fahre zu %d", NEXT_FLOOR);
      HALTED = 0;
    end
  end   
  
   initial  begin
     $display("\t\ttime,\treset,\tfloor,\tdisplayed_floor,\tnext_floor"); 
     $monitor("%d,\t%b,\t%d,\t%c,\t%c,\t%d",$time, RESET,CURRENT_FLOOR,ASCI_FLOOR_HIGH,ASCI_FLOOR_LOW,NEXT_FLOOR); 
   end       
      
   always @(expected_next_floor) begin
     if(expected_next_floor == NEXT_FLOOR) begin
       $display("Naechstes Stockwerk korrekt berechnet - Fahre nach %d", NEXT_FLOOR);
     end
     else begin
       $display("FEHLER: Naechstes Stockwerk ist %d und nicht %d", NEXT_FLOOR, expected_next_floor);     
     end
   end  
  //***********************************************************// 

  //*********************    FUNCTIONS     *********************// 
  //ceil of the log base 2
  function integer ceil_log2;
    input [31:0] value;
    for (ceil_log2=0; value>0; ceil_log2=ceil_log2+1)
      value = value>>1;
  endfunction
  
  // value cannot be less than 1
  function integer min_1;
    input [31:0] value;
    if (value == 0)
      min_1 = 1;
    else
      min_1 = value;
  endfunction  
  //************************************************************// 
      
endmodule
